/*
 * Copyright (c) 2016-2021, Hangzhou Qulian Technology Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ecc.h"
#include "ql_type.h"
#include "CuTest.h"


// Declare internal functions
OhErrType uECC_create_pubkey(OhByteType p_publicKey[uECC_BYTES*2], OhByteType p_privateKey[uECC_BYTES]);
OhErrType uECC_verify(OhBoolType* res, const OhByteType p_publicKey[uECC_BYTES*2], const OhByteType p_hash[uECC_BYTES], const OhByteType p_signature[uECC_BYTES*2]);
OhErrType uECC_make_key(OhByteType p_publicKey[uECC_BYTES*2], OhByteType p_privateKey[uECC_BYTES]);
OhErrType uECC_shared_secret(const OhByteType p_publicKey[uECC_BYTES*2], const OhByteType p_privateKey[uECC_BYTES], OhByteType p_secret[uECC_BYTES]);


OhVoidType print_ecc_data(OhByteType *out,  OhByteType *buf, OhSizeType len){
    OhByteType i;
    for(i = 0; i < len; i ++){
        buf[i*2]   = ((out[i] & 0xf0) >> 4) <=9 ?  ((out[i] & 0xf0) >> 4) + '0' : ((out[i] & 0xf0) >> 4) + 'a' - 10;
        buf[i*2+1] = ((out[i] & 0x0f)     ) <=9 ?  ((out[i] & 0x0f)     ) + '0' : ((out[i] & 0x0f)     ) + 'a' - 10;
    }
    buf[len*2] = '\0';
}

void sign_test(CuTest* tc){
    OhUint8Type hash[32]  = {0xed,0x97,0x8a,0x28,0xe0,0x9f,0xd1,0x38,
                            0x5f,0xc1,0x4d,0x94,0x17,0x46,0xa9,0x47,
                            0x61,0x4c,0x92,0xb3,0x9d,0x59,0x07,0x02,
                            0x27,0xf4,0xcd,0xd3,0xb5,0xc9,0x94,0x24};
    OhUint8Type private[32] = {2,2,2,1,1,1,1,1,   1,1,1,1,1,1,1,1,   1,1,1,1,1,1,1,1,   1,1,1,1,1,1,1,1};
    OhUint8Type sig[65], out[256];
    OhInt32Type res = uECC_sign(private, hash, sig);
    CuAssertIntEquals(tc, ECC_SUCCESS, res);
    print_ecc_data(sig, out, 65);
    CuAssertStrEquals(tc, "dfcc21d389e6614ed98702ebdfaaf06335ee3d10f0449beca6430ac4c53aa23203832a4f0d12ece5eb9969b6f0d14e86dc8e6a49c47d58c19661c8d4d888cdc701",(char*)out);
}

void create_pubkey_test(CuTest* tc){
    OhUint8Type private[32] = {2,2,2,1,1,1,1,1,   1,1,1,1,1,1,1,1,   1,1,1,1,1,1,1,1,   1,1,1,1,1,1,1,1};
    OhUint8Type public[64],public_str[256];
    OhInt32Type res = uECC_create_pubkey(public,private);
    CuAssertIntEquals(tc, ECC_SUCCESS, res);
    print_ecc_data(public,public_str, 64);
    CuAssertStrEquals(tc, "c542b416ff48d81be1651f25548c06d191cfa30cb81af51911830cf2e012cc9ff7fd76ddd1594c607160a97904331519240063916d8f361def5389730ac2d744",(char*)public_str);
}

void verify_test(CuTest* tc){
    OhUint8Type hash[32]  = {0xed,0x97,0x8a,0x28,0xe0,0x9f,0xd1,0x38,
                            0x5f,0xc1,0x4d,0x94,0x17,0x46,0xa9,0x47,
                            0x61,0x4c,0x92,0xb3,0x9d,0x59,0x07,0x02,
                            0x27,0xf4,0xcd,0xd3,0xb5,0xc9,0x94,0x24};
    OhUint8Type sig[65]  =  {0xdf,0xcc,0x21,0xd3,0x89,0xe6,0x61,0x4e,
                            0xd9,0x87,0x02,0xeb,0xdf,0xaa,0xf0,0x63,
                            0x35,0xee,0x3d,0x10,0xf0,0x44,0x9b,0xec,
                            0xa6,0x43,0x0a,0xc4,0xc5,0x3a,0xa2,0x32,
                            0x03,0x83,0x2a,0x4f,0x0d,0x12,0xec,0xe5,
                            0xeb,0x99,0x69,0xb6,0xf0,0xd1,0x4e,0x86,
                            0xdc,0x8e,0x6a,0x49,0xc4,0x7d,0x58,0xc1,
                            0x96,0x61,0xc8,0xd4,0xd8,0x88,0xcd,0xc7,
                            0x01};
    OhUint8Type pub[64]  = {0xc5,0x42,0xb4,0x16,0xff,0x48,0xd8,0x1b,
                            0xe1,0x65,0x1f,0x25,0x54,0x8c,0x06,0xd1,
                            0x91,0xcf,0xa3,0x0c,0xb8,0x1a,0xf5,0x19,
                            0x11,0x83,0x0c,0xf2,0xe0,0x12,0xcc,0x9f,
                            0xf7,0xfd,0x76,0xdd,0xd1,0x59,0x4c,0x60,
                            0x71,0x60,0xa9,0x79,0x04,0x33,0x15,0x19,
                            0x24,0x00,0x63,0x91,0x6d,0x8f,0x36,0x1d,
                            0xef,0x53,0x89,0x73,0x0a,0xc2,0xd7,0x44};
    OhBoolType verify_res = 0;
    OhErrType res = uECC_verify(&verify_res, pub, hash, sig);
    CuAssertIntEquals(tc, ECC_SUCCESS, res);
    CuAssertIntEquals(tc, 1, verify_res);
}

void shared_secret_test(CuTest* tc){
    OhUint8Type public[64]  = {0xc5,0x42,0xb4,0x16,0xff,0x48,0xd8,0x1b,
                           0xe1,0x65,0x1f,0x25,0x54,0x8c,0x06,0xd1,
                           0x91,0xcf,0xa3,0x0c,0xb8,0x1a,0xf5,0x19,
                           0x11,0x83,0x0c,0xf2,0xe0,0x12,0xcc,0x9f,
                           0xf7,0xfd,0x76,0xdd,0xd1,0x59,0x4c,0x60,
                           0x71,0x60,0xa9,0x79,0x04,0x33,0x15,0x19,
                           0x24,0x00,0x63,0x91,0x6d,0x8f,0x36,0x1d,
                           0xef,0x53,0x89,0x73,0x0a,0xc2,0xd7,0x44};
    OhUint8Type private[32] = {2,2,2,1,1,1,1,1,   1,1,1,1,1,1,1,1,   1,1,1,1,1,1,1,1,   1,1,1,1,1,1,1,1};

    OhUint8Type ciphertextPrivKey[32] = {0};
    OhUint8Type ciphertextPubKey[64] = {0};
    OhUint8Type p_secret1[32] = {0}, p_secret1_str[256];
    OhUint8Type p_secret2[32] = {0}, p_secret2_str[256];
    OhErrType res = uECC_make_key(ciphertextPubKey, ciphertextPrivKey);
    CuAssertIntEquals(tc, ECC_SUCCESS, res);

    res = uECC_shared_secret(public,ciphertextPrivKey,p_secret1); // Encryption
    CuAssertIntEquals(tc, ECC_SUCCESS, res);
    // Use the sharedECCKey for symmetric encryption.
    // ... Transmit ciphertextPubKey and ciphertext ...
    // Use the ciphertextPubKey for symmetric encryption.
    res = uECC_shared_secret(ciphertextPubKey,private,p_secret2); // Decryption
    CuAssertIntEquals(tc, ECC_SUCCESS, res);

    print_ecc_data(p_secret1,p_secret1_str,32);
    print_ecc_data(p_secret2,p_secret2_str,32);
    CuAssertStrEquals(tc,(char*)p_secret1_str,(char*)p_secret2_str);

}

CuSuite* ecc_test_suite(void)
{
    CuSuite* suite = CuSuiteNew();
    SUITE_ADD_TEST(suite, sign_test);
    SUITE_ADD_TEST(suite, create_pubkey_test);
    SUITE_ADD_TEST(suite, verify_test);
    SUITE_ADD_TEST(suite, shared_secret_test);
    return suite;
}
